defmodule DmCypherLib do
	@moduledoc false
	@struct_field :__struct__

	# %{}  =>  (node:LABEL{})
	def insert_cypher(struct) when Kernel.is_struct(struct) do
		mod = Map.get(struct, @struct_field)
		label = mod.label()
		alias = mod.alias()

		str = if alias == nil do
			"(#{String.downcase(label)}:#{label}"
		else
			"(#{String.downcase(label)}:#{label}:#{alias}"
		end
		{kv_str, args} = kv_cypher(MapLib.drop_key(struct, [@struct_field]))
		{str <> "{" <> kv_str <> "})", args}
	end

	def insert_cypher2(mod, args) do
		label = mod.label()
		alias = mod.alias()
		str = if alias == nil do
			"(#{String.downcase(label)}:#{label}"
		else
			"(#{String.downcase(label)}:#{label}:#{alias}"
		end
		{kv_str, _args} = kv_cypher(args)
		str <> "{" <> kv_str <> "})"
	end

	def insert_cypher(mod, args) do
		mod.new(args)
		|> insert_cypher()
	end

	def set_str(args, node_) do
		str = update_cypher_(args, node_)
		if str != "" do
			"set " <> str
		else
			""
		end
	end

	def update_cypher_(kvs, node_name) do
		Enum.map_join(kvs, ", ", &("#{node_name}.#{elem(&1, 0)} = $#{elem(&1, 0)}"))
	end

	def kv_cypher(nil) do
		{"", %{}}
	end
	def kv_cypher(kvs) do
		{str, v} = Enum.reduce(
			kvs,
			{nil, %{}},
			fn ({key, v}, {keys_acc, val_acc}) ->
				str = "#{key}:$#{key}"
				if keys_acc == nil do
					{str, Map.put(val_acc, key, to_str(v))}
				else
					{str <> "," <> keys_acc, Map.put(val_acc, key, to_str(v))}
				end
			end
		)
	end
	def kv_cypher(kvs, constant) do
		Enum.map_join(
			kvs,
			", ",
			fn ({key, value}) ->
				case Map.get(constant, key) do
					nil ->
						key <> ": " <> inspect(to_str(value))
					value1 ->
						key <> ": " <> value1
				end
			end
		)
		|> case do
			   "" ->
				   ""
			   str ->
				   "{#{str}}"
		   end
	end

	def to_str(v) do
		cond do
			is_struct(v, Date) || is_struct(v, NaiveDateTime) ->
				v
			is_map(v) ->
				Jason.encode!(v)
			true ->
				v
		end
	end

end
